package com.aispeech.asr.comm.utils;

import com.aispeech.asr.comm.enums.APICode;
import com.aispeech.asr.comm.exceptions.AsrException;
import lombok.extern.slf4j.Slf4j;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.*;
import java.util.concurrent.*;

import org.apache.commons.io.IOUtils;

@Slf4j
public class ShellUtils {
    public static long timeoutByAudioLength(long length) {
        long mbFive = 5242880L;                // 5M
        long secsBase = 30;                    // 30secs

        long secs = secsBase * Math.min(60, length / mbFive);
        secs = Math.max(secs, 180);// 最少3分钟

        return secs;          //比task handler少30s，防止handler提前超时
    }

    public static long timeoutByDuration(long duration) {
        long fiveMins = 300;           // 5Min
        long secsBase = 60;          // 60secs

        long secs = secsBase * Math.min(30, duration / (fiveMins * 1000));
        secs = Math.max(secs, 180);// 最少3分钟

        return secs;
    }

    public static void exec(String dir, List<String> command) throws AsrException {
        exec(dir, 30, TimeUnit.SECONDS, command, new HashMap<>());
    }
    
    public static void exec(String dir, long timeout, TimeUnit unit, List<String> command) throws AsrException {
        exec(dir, timeout, unit, command, new HashMap<>());
    }

    public static void exec(String dir, long timeout, TimeUnit unit, List<String> command, Map<String,String> resultMap) throws AsrException {
        int ret = -1;
        FutureTask<Integer> future = new FutureTask<>(new ShellCallable(dir, command, resultMap));
        new Thread(future).start();

        try {
            ret = future.get(timeout, unit);
        } catch (InterruptedException | ExecutionException | TimeoutException e) {
            log.warn(String.format("exec cmd: %s failed.", command), e);
            throw new AsrException(APICode.INTERNAL_ERROR.getCode(), e.getClass().getName());
        }

        if (ret != 0) {
            String message = String.format("exec cmd: %s failed, ret: %d.", command, ret);
            log.warn(message);
            throw new AsrException(APICode.INTERNAL_ERROR.getCode(), message);
        }
    }

    static class ShellCallable implements Callable<Integer> {

        private final String dir;
        private final List<String> command;
        private Map<String,String> resultMap;

        public ShellCallable(String dir, List<String> command) {
            this.dir = dir;
            this.command = command;
        }

        public ShellCallable(String dir, String... command) {
            this.dir = dir;

            this.command = new ArrayList<>(command.length);
            for (String arg : command)
                this.command.add(arg);
        }
        
        public ShellCallable(String dir, List<String> command, Map<String,String> resultMap) {
            this.dir = dir;
            this.command = command;
            this.resultMap = resultMap;
        }

        @Override
        public Integer call() throws Exception {
            ProcessBuilder builder = new ProcessBuilder(command);
            Process process = builder.directory(new File(dir)).start();
            
            int ret = process.waitFor();
            
            InputStream errorStream = process.getErrorStream();
            InputStream inputStream = process.getInputStream();
			String errorMsg = IOUtils.toString(errorStream, "UTF-8");
			String resultMsg = IOUtils.toString(inputStream, "UTF-8");
			
			resultMap.put("errorMsg", errorMsg);
			resultMap.put("resultMsg", resultMsg);
            
            return ret;
        }
    }

}
